package com.itbird.eventbus.core;

import android.annotation.SuppressLint;
import android.os.Looper;
import android.util.Log;

import com.itbird.eventbus.annotation.Subscribe;
import com.itbird.eventbus.annotation.ThreadMode;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * EventBus 对外单例类
 * Created by itbird on 2022/9/19
 */
public class EventBus {
    private static final String TAG = EventBus.class.getSimpleName();
    private static volatile EventBus eventBus;
    // 将SubscribeMethod，转换为map存储，key为MsgEventClass，value为Subscribe注解的所有方法的封装对象
    // 方便post事件时，可以直接根据事件classtype，找到对应的方法和对象，从而实现反射调用
    private Map<Class<?>, CopyOnWriteArrayList<SubscribeMethod>> subscribeMethodMaps = new HashMap<>();
    //主线程
    private MainThreadSupport mainThreadSupport;
    private Poster mainPoster;
    private Poster backgroundPoster;

    private EventBus() {
        mainThreadSupport = new MainThreadSupport.AndroidHandlerMainThreadSupport(Looper.getMainLooper());
        mainPoster = mainThreadSupport.createPoster(this);
        backgroundPoster = new BackgroundPoster(this);

    }

    public static EventBus getIntance() {
        if (eventBus == null) {
            synchronized (EventBus.class) {
                if (eventBus == null) {
                    eventBus = new EventBus();
                }
            }
        }
        return eventBus;
    }


    /**
     * 发送事件
     *
     * @param t
     * @param <T>
     */
    public <T> void postEvent(T t) {
        //todo 发送事件,遍历注解，找到事件相关的对象和方法，然后反射调用其方法
        if (subscribeMethodMaps.isEmpty()) {
            return;
        }
        if (subscribeMethodMaps.containsKey(t.getClass())) {
            List<SubscribeMethod> subscribeMethodList = subscribeMethodMaps.get(t.getClass());
            for (SubscribeMethod subscribeMethod : subscribeMethodList) {
                reflectCallSubscribeMethod(t, subscribeMethod);
            }
        }
    }

    /**
     * 反射调用方法
     *
     * @param t               入参，消息事件
     * @param subscribeMethod 具体反射调用的方法与对象
     * @param <T>
     */
    public <T> void reflectCallSubscribeMethod(T t, SubscribeMethod subscribeMethod) {
        Method method = subscribeMethod.getMethod();
        switch (subscribeMethod.getThreadMode()) {
            case MAIN_THREAD:
                //如果是在当前线程执行，则直接执行
                if (mainThreadSupport.isMainThread()) {
                    invokeSubscribeMethod(subscribeMethod, t);
                } else {
                    mainPoster.enqueue(subscribeMethod, t);
                }
                break;
            case BACKGROUND:
                if (mainThreadSupport.isMainThread()) {
                    backgroundPoster.enqueue(subscribeMethod, t);
                } else {
                    invokeSubscribeMethod(subscribeMethod, t);
                }
                break;
        }
    }


    /**
     * 注册消息
     *
     * @param t
     * @param <T>
     */
    public <T> void register(T t) {
        //todo 需要将当前对象中，所有Subscribe注解的方法，遍历处理
        //1.需要获取当前类中，所有方法（包含私有、公有，但是不包含继承）
        Method[] methods = t.getClass().getDeclaredMethods();
        //2.遍历此类中所有的方法，找到有Subscribe注解的方法，然后单独处理
        for (Method method : methods) {
            if (method.isAnnotationPresent(Subscribe.class)) {
                handleSubscribeAnnotation(t, method);
            }
        }
    }


    /**
     * 处理Subscribe注解的方法，将其整理存储为map数据结构
     *
     * @param t
     * @param method
     * @param <T>
     */
    @SuppressLint("NewApi")
    private <T> void handleSubscribeAnnotation(T t, Method method) {
        //获取到method注解的值
        ThreadMode threadMode = method.getAnnotation(Subscribe.class).threadMode();
        //参数等于1，才可以进行处理
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (parameterTypes.length == 1) {
            //封装为实体类
            SubscribeMethod subscribeMethod = new SubscribeMethod(t, method, threadMode);
            //3.由于后续发送事件时，是post(Event)，所以需要将SubscribeMethod放入一个map集合中
            CopyOnWriteArrayList<SubscribeMethod> subscribeMethodList = subscribeMethodMaps.getOrDefault(parameterTypes[0], new CopyOnWriteArrayList<>());
            subscribeMethodList.add(subscribeMethod);
            subscribeMethodMaps.put(parameterTypes[0], subscribeMethodList);
        }
    }

    /**
     * 注销事件
     *
     * @param t
     * @param <T>
     */
    public <T> void unregister(T t) {
        // todo 实现事件的注销
        if (subscribeMethodMaps.isEmpty()) {
            return;
        }

        Iterator<Map.Entry<Class<?>, CopyOnWriteArrayList<SubscribeMethod>>> classListEntry = subscribeMethodMaps.entrySet().iterator();
        while (classListEntry.hasNext()) {
            Map.Entry<Class<?>, CopyOnWriteArrayList<SubscribeMethod>> entry = classListEntry.next();
            List<SubscribeMethod> subscribeMethodList = entry.getValue();
            for (int i = subscribeMethodList.size() - 1; i >= 0; i--) {
                Log.d(TAG, "unregister subscribeMethodList.get(i).getObject().getClass() = " + subscribeMethodList.get(i).getObject().getClass());
                Log.d(TAG, "unregister t.getClass() = " + t.getClass());
                Log.d(TAG, "subscribeMethodList.get(i) = " + subscribeMethodList.get(i));
                if (subscribeMethodList.get(i).getObject().getClass() == t.getClass()) {
                    subscribeMethodList.remove(i);
                }
            }
            if (subscribeMethodList.size() <= 0) {
                classListEntry.remove();
            }
        }
    }

    public <T> void invokeSubscribeMethod(SubscribeMethod subscribeMethod, T t) {
        try {
            subscribeMethod.getMethod().invoke(subscribeMethod.getObject(), t);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
